//
//  GCDGroupTableController.m
//  ThreadDemo
//
//  Created by 苏沫离 on 2018/7/16.
//  Copyright © 2018年 苏沫离. All rights reserved.
//
#define CellIdentifer @"UITableViewCell"

#import "GCDGroupTableController.h"

@interface GCDGroupTableController ()
@property (nonatomic ,strong) NSArray<NSString *> *titleArray;
@end

@implementation GCDGroupTableController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // Uncomment the following line to preserve selection between presentations.
    // self.clearsSelectionOnViewWillAppear = NO;
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:CellIdentifer];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.titleArray.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifer forIndexPath:indexPath];
    cell.textLabel.text = self.titleArray[indexPath.row];
    cell.textLabel.numberOfLines = 0;
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    NSLog(@"-------- 开始处理 --------");

    if (indexPath.row == 0)
    {
        [self dispatchGroupMethod];
    }
    else if (indexPath.row == 1)
    {
        [self dispatch_group_waitMethod];
    }
    else if (indexPath.row == 2)
    {
        [self dispatch_group_Enter_Leave_Method];
    }
    else if (indexPath.row == 3)
    {
        [self holdThreadCountMethod];
    }
    else if (indexPath.row == 4)
    {
        [self dispatch_group_async_f_Method];
    }
    else if (indexPath.row == 5)
    {
        [self dispatch_barrier_method];
    }
    NSLog(@"-------- 结束处理 --------");

}

- (void)dispatchGroupMethod
{
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("com.demo.task", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_apply(3, queue, ^(size_t index) {
        dispatch_group_async(group, queue, ^{
            NSLog(@"开始执行：task%zu ======= %@",index,NSThread.currentThread);
            [NSThread sleepForTimeInterval:3];//模拟耗时任务
            NSLog(@"结束执行：task%zu ------- %@",index,NSThread.currentThread);
        });
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"****** 所有任务结束 ****** %@",NSThread.currentThread);
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"开始执行：task3 ======= %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        NSLog(@"结束执行：task3 ------- %@",NSThread.currentThread);
    });
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"将要执行：task4  ======= %@",NSThread.currentThread);
        dispatch_group_async(group, queue, ^{
            NSLog(@"开始执行：task4 ======= %@",NSThread.currentThread);
            [NSThread sleepForTimeInterval:3];//模拟耗时任务
            NSLog(@"结束执行：task4 ------- %@",NSThread.currentThread);
        });
    });
}


- (void)dispatch_group_waitMethod
{
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("com.demo.task", DISPATCH_QUEUE_CONCURRENT);

    dispatch_apply(3, queue, ^(size_t index) {
        dispatch_group_async(group, queue, ^{
            NSLog(@"开始执行：task%zu ======= %@",index,NSThread.currentThread);
            [NSThread sleepForTimeInterval:3];//模拟耗时任务
            NSLog(@"结束执行：task%zu ------- %@",index,NSThread.currentThread);
        });
    });
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"将要执行：task3  ======= %@",NSThread.currentThread);
        dispatch_group_async(group, queue, ^{
            NSLog(@"开始执行：task3 ======= %@",NSThread.currentThread);
            [NSThread sleepForTimeInterval:3];//模拟耗时任务
            NSLog(@"结束执行：task3 ------- %@",NSThread.currentThread);
        });
    });
    
    NSLog(@"执行到此 ======= %@",NSThread.currentThread);
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
}

/*

 
 */

/*
 dispatch_group_enter 标志着一个任务追加到 group，执行一次，相当于 group 中未执行完毕任务数+1
 dispatch_group_leave 标志着一个任务离开了 group，执行一次，相当于 group 中未执行完毕任务数-1。
 当 group 中未执行完毕任务数为0的时候，才会使dispatch_group_wait解除阻塞，以及执行追加到dispatch_group_notify中的任务。
 */
- (void)dispatch_group_Enter_Leave_Method
{
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("com.demo.task", DISPATCH_QUEUE_CONCURRENT);

    dispatch_apply(3, queue, ^(size_t index) {
        dispatch_group_enter(group);//标志着一个任务追加到 group
        dispatch_async(queue, ^{
            NSLog(@"开始执行：task%zu ======= %@",index,NSThread.currentThread);
            [NSThread sleepForTimeInterval:3];//模拟耗时任务
            NSLog(@"结束执行：task%zu ------- %@",index,NSThread.currentThread);
            dispatch_group_leave(group);//标志着一个任务离开了 group
        });
    });

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"****** 所有任务结束 ****** %@",NSThread.currentThread);
    });
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"将要执行：task3  ======= %@",NSThread.currentThread);
        dispatch_group_enter(group);
        dispatch_async(queue, ^{
            NSLog(@"开始执行：task3 ======= %@",NSThread.currentThread);
            [NSThread sleepForTimeInterval:3];//模拟耗时任务
            NSLog(@"结束执行：task3 ------- %@",NSThread.currentThread);
            dispatch_group_leave(group);//标志着一个任务离开了 group
        });
    });

    /*
     从dispatch_group_enter、dispatch_group_leave相关代码运行结果中可以看出：
     当所有任务执行完成之后，才执行 dispatch_group_notify 中的任务。
     这里的dispatch_group_enter、dispatch_group_leave组合，其实等同于dispatch_group_async。
     */
}

/*
 */


/*
 利用 GCD 的 dispatch_group_t 和 semaphore 功能，我们可以做到控制线程数量，并且在所有任务执行完成之后得到通知。
 */
- (void)holdThreadCountMethod
{
    dispatch_queue_t queue = dispatch_queue_create("com.objective_c_language.gcd.ThreadCount", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
    
    dispatch_apply(10, queue, ^(size_t i) {
        
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        
        // 只有当信号量大于 0 的时候，线程将信号量减 1，程序向下执行
        // 否则线程会阻塞并且一直等待，直到信号量大于 0
        dispatch_group_async(group, queue, ^{
            NSLog(@"执行第 %zu 次任务 == %@",i,NSThread.currentThread);
            [NSThread sleepForTimeInterval:10];//处理耗时任务
            dispatch_semaphore_signal(semaphore);// 当线程任务执行完成之后，发送一个信号，增加信号量。
        });
    });
    
    //既控制了线程数量，也在执行任务完成之后得到了通知。
    dispatch_group_notify(group, queue, ^{
        NSLog(@"任务结束 ------ %@",NSThread.currentThread);
    });
}


void dispatch_function_test(void *_Nullable context)
{
    id object = (__bridge id)context;
    
    NSLog(@"%@ == %@",object,NSThread.currentThread);
}

void dispatch_function_notify(void *_Nullable context)
{
    id object = (__bridge id)context;
    
    NSLog(@"%@ == %@",object,NSThread.currentThread);
}

- (void)dispatch_group_async_f_Method
{
    /* 将应用程序定义的函数提交给分派队列，并将其与指定的分派组关联。
     void dispatch_group_async_f(dispatch_group_t group, dispatch_queue_t queue, void *_Nullable context,  dispatch_function_t work);
     
     参数：
     dispatch_group_t group 将提交的函数与之关联的分派组。系统将保留组，直到应用程序定义的函数运行到完成为止。这个参数不能为空。
     dispatch_queue_t queue 为异步调用提交函数的分派队列。该队列由系统保留，直到应用程序定义的函数运行到完成为止。这个参数不能为空。
     void *_Nullable context 将应用程序定义的上下文参数传递给应用程序定义的函数。
     dispatch_function_t work 在目标队列上调用的应用程序定义的函数。传递给这个函数的第一个参数是上下文参数中的值。
     */
    
    NSLog(@"开始处理 ======= %@",NSThread.currentThread);

    dispatch_queue_t queue = dispatch_queue_create("dispatch_group_async_f_Method", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    
    //分派异步的 C 函数到一个任务组来使用 dispatch_group_async_f 函数
    dispatch_group_async_f(group, queue, @"这是个不确定类型参数", dispatch_function_test);
    
    //既控制了线程数量，也在执行任务完成之后得到了通知。
    dispatch_group_notify_f(group, dispatch_get_main_queue(), @"这是个不确定类型参数", dispatch_function_notify);
    NSLog(@"结束处理 ======= %@",NSThread.currentThread);

}

/*
 dispatch_barrier_sync 与 dispatch_barrier_async
 相同点：都是将 dispatch_queue_t 一分为2，前面的任务执行完，才执行 自己 block 中的任务
 不同点：
 （1）、dispatch_barrier_sync 与 dispatch_sync 一样，将指定的Block同步追加到指定的Dispatch Queue中，
 dispatch_barrier_sync 函数 block中的代码与 dispatch_barrier_sync 函数上下文的代码处于同一线程
 （2）、dispatch_barrier_async 与 dispatch_async 一样，
 dispatch_barrier_sync 函数 block中的代码与 dispatch_barrier_sync 函数上下文的代码处于不同线程
 */
- (void)dispatch_barrier_method
{
    NSLog(@"开始处理 ======= %@",NSThread.currentThread);

    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t testQueue = dispatch_queue_create("com.objective_c_language.gcd.test6", DISPATCH_QUEUE_CONCURRENT);
    
    //使用 for 循环：每次取出一个元素，逐个遍历；dispatch_apply 可以同时遍历多个数字
    dispatch_apply(10, testQueue, ^(size_t index) {
        dispatch_group_async(group, testQueue, ^{
            NSLog(@"开始执行第 %zu 次任务 == %@",index,NSThread.currentThread);
            [NSThread sleepForTimeInterval:10];//模拟耗时任务
            NSLog(@"结束执行第 %zu 次任务 -- %@",index,NSThread.currentThread);
        });
    });
    
    
    //将所有的任务都加入 group ，等待所有的任务执行完成后，dispatch_group_notify 会被调用。
    //回到主线程接收前面所有任务完成的通知
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_group_notify(group, mainQueue, ^{
        NSLog(@"****** 所有任务结束 ****** %@",NSThread.currentThread);
    });

    dispatch_barrier_async(testQueue, ^{
        NSLog(@"****** 10个任务结束 ****** %@",NSThread.currentThread);
    });
    
    dispatch_group_async(group, testQueue, ^{
        NSLog(@"最后一次任务 == %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:20];//模拟耗时任务
        NSLog(@"最后一次任务 -- %@",NSThread.currentThread);
    });
    NSLog(@"结束处理 ======= %@",NSThread.currentThread);
}

/*
 首先完成 dispatch_apply 里面的 10 个耗时任务，然后调用 dispatch_barrier_async 的方法，执行block 的任务，最后开始执行 最后一次任务，所有任务结束后，dispatch_group_notify 收到通知
 最后一个任务所在的线程为前面任务 已开辟的线程： 处理并发异步任务时候线程是可以循环往复使用的，比如任务1的线程执行完了任务1，线程可以接着去执行后面没有执行的任务
 */

#pragma mark - setter and getter

- (NSArray<NSString *> *)titleArray
{
    if (_titleArray == nil)
    {
        _titleArray = @[@"上传 10 张图片，全部上传完成后通知用户",@"dispatch_group_waitMethod",
                        @"dispatch_group_Enter_Leave_Method",@"控制线程数量",@"dispatch_group_async_f",
                        @"group_barrier"];
        
    }
    return _titleArray;
}
@end
